package com.ruoyi.flow.auth.security;

import com.ruoyi.flow.auth.security.FlowUser;
import com.ruoyi.flow.common.config.Global;
import com.google.common.collect.Lists;
import org.activiti.engine.impl.identity.Authentication;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * @Author: wtian
 * @Description:
 * @Date: Created in 9:45 2018/8/10
 * @Version: 1.0
 */
@Service
public class FlowUserDetailService implements UserDetailsService {
    @Autowired
    private IdentityService identityService;
    @Resource
    private UserCache userCache;

    private long userValidityPeriod;

    @Override
    @Transactional
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        checkArgument(StringUtils.isNotEmpty(username), "username must not be null.");
        User user = identityService.createUserQuery().userId(username).singleResult();
        if (Objects.isNull(user)) {
            throw new UsernameNotFoundException("user " + username + " not exist.");
        }
        List<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
        List<Group> groups = identityService.createGroupQuery().groupMember(username).list();
        for (Group group : groups) {
            grantedAuthorities.add(new SimpleGrantedAuthority(group.getId()));
        }

        String superUserGroupName = Global.getString("admin.group", null);
        for (Group group : this.identityService.createGroupQuery().groupMember(user.getId()).list()) {
            if (StringUtils.equals(superUserGroupName, group.getName())) {
                grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
            }
        }

        userCache.putUserInCache(username, new UserCache.CachedUser(user, grantedAuthorities));

        Authentication.setAuthenticatedUserId(user.getId());

        return new com.ruoyi.flow.auth.security.FlowUser(user, grantedAuthorities);
    }

    @Transactional
    public UserDetails loadByUserId(String userId) {
        UserCache.CachedUser cachedUser = this.userCache.getUser(userId, true, true, false);
        if (cachedUser == null) {
            throw new UsernameNotFoundException("User " + userId + " was not found in the database");
        }
        long lastDatabaseCheck = cachedUser.getLastDatabaseCheck();
        long currentTime = System.currentTimeMillis();
        if ((this.userValidityPeriod <= 0L) || (currentTime - lastDatabaseCheck >= this.userValidityPeriod)) {
            this.userCache.invalidate(userId);
            cachedUser = this.userCache.getUser(userId, true, true, false);
            cachedUser.setLastDatabaseCheck(currentTime);
        }
        User user = cachedUser.getUser();
        Authentication.setAuthenticatedUserId(user.getId());

        return new FlowUser(user, cachedUser.getGrantedAuthorities());
    }

    public long getUserValidityPeriod() {
        return userValidityPeriod;
    }

    public void setUserValidityPeriod(long userValidityPeriod) {
        this.userValidityPeriod = userValidityPeriod;
    }
}
